/*
    Code : Dzlua
    Email: 505544956@qq.com
    Time : 2017/06/14
*/
#include <iostream>
#include <iomanip>
using namespace std;

#include <dzlua/dzlua.h>
using namespace dzlua;
#include <jsoncpp/value.h>
#include <jsoncpp/reader.h>
#include <jsoncpp/writer.h>

#include <Windows.h>

#define DZLUA_CONFILE "config.json"

void ShowHelp(const Json::Value& cfg);
bool ReadConfig(Json::Value& cfg);
bool ParserAction(const Json::Value& cfg, char *args[], int len);

int main(int argc, char *argv[])
{
  cout << "Welcome to use dzlua!" << endl;

  Json::Value cfg;
  // read config
  if (!ReadConfig(cfg)) {
    cerr << "Failed to read config file!" << endl;
    return 0;
  }

  if (argc < 2) {
    ShowHelp(cfg);
    return 0;
  }

  {
    for (int i = 1; i < argc; ++i) {
      if (0 == strcmp(argv[i], "-h") ||
            0 == strcmp(argv[i], "--help") ||
            0 == strcmp(argv[i], "-?")) {
        ShowHelp(cfg);
        return 0;
      }
    }
  }

  // generate cmdline
  if (!ParserAction(cfg, &argv[1], argc - 1)) {
    return 0;
  }

  return 0;
}

//------------------------------------------//
struct sActionInfo {
  std::string actname;
  std::string name;
  std::string type;
  std::string program;
  std::string arg;
};

bool _ParserActionInfo(const std::string& actname, const Json::Value& acts, char *args[], int len);
bool _ParserInfo(const std::string& actname, const Json::Value& info, char *args[], int len);
bool _DoAction(const sActionInfo& info);

bool _ActionShell(const sActionInfo& info);
bool _ActionExplorer(const sActionInfo& info);
bool _ActionGn(const sActionInfo& info);
bool _ActionNinja(const sActionInfo& info);
//------------------------------------------//
void ShowHelp(const Json::Value& cfg) {
  cout << endl << "Usage: dzlua action option [args]" << endl << endl;
  for (auto it = cfg.begin(); it != cfg.end(); ++it) {
    cout << "  " << it.memberName() << " options->" << endl;
    Json::Value opts = *it;
    for (auto opt = opts.begin(); opt != opts.end(); ++opt) {
      Json::Value info = *opt;
      cout << std::setw(15)<< info["name"].asString() << " : " << base::UToA(info["msg"].asString()) << endl;
    }
  }
}

bool ReadConfig(Json::Value& cfg) {
  char szPath[MAX_PATH];
  if( !::GetModuleFileNameA( NULL, szPath, MAX_PATH ) ) {
    cerr << "GetModuleFileName failed (" << ::GetLastError() << ")" << endl;
    return false;
  }
  std::unique_ptr<FilePath> file = FilePath::NewFilePath(szPath);
  file = file->dir();
  file->append(DZLUA_CONFILE);
  
  std::string cfgstr= FilePath::ReadFile(file->string());
  Json::Reader reader;
  return reader.parse(cfgstr, cfg);
}

bool ParserAction(const Json::Value& cfg, char *args[], int len) {
  std::string actname = args[0];
  Json::Value acts = cfg[actname];
  if (!acts.isArray()) {
    cerr << "No action " << actname << endl;
    return false;
  }

  if (len < 2) {
    cerr << "No action " << actname << " args." << endl;
    return false;    
  }
  
  return _ParserActionInfo(actname, acts, &args[1], len - 1);
}

bool _ParserActionInfo(const std::string& actname, const Json::Value& acts, char *args[], int len) {
  std::string proname = args[0];
  for (auto it = acts.begin(); it != acts.end(); ++it) {
    if ((*it)["name"].asString() == proname) {
      if (len < 2) {
        return _ParserInfo(actname, *it, nullptr, 0);
      } else {
        return _ParserInfo(actname, *it, &args[1], len - 1);
      }
    }
  }

  cerr << "No action " << actname << " " << proname << endl;
  return false;
}

bool _ParserInfo(const std::string& actname, const Json::Value& jsinfo, char *args[], int len) {
  sActionInfo info;
  // name must exist.
  info.actname = base::UToA(actname);
  info.name = base::UToA(jsinfo["name"].asString());
  info.type = base::UToA(jsinfo["type"].asString());
  info.program = base::UToA(jsinfo["program"].asString());

  std::vector<std::string> v;
  for (auto it = jsinfo["args"].begin(); it != jsinfo["args"].end(); ++it) {
    v.push_back((*it).asString());
  }
  
  if (args && len > 0) {
    for (int i = 0; i < len; ++i) {
      v.push_back(args[i]);
    }
  }
  info.arg = base::UToA(base::unsplit(v, " "));

  return _DoAction(info);
}

bool _DoAction(const sActionInfo& info) {
  if (info.type.empty() || info.program.empty()) {
    cerr << "Error action " << info.actname << " " << info.name << endl;
    return false;
  }

  if (info.type == "shell") {
    return _ActionShell(info);
  } else if (info.type == "explorer") {
    return _ActionExplorer(info);
  } else if (info.type == "gn") {
    return _ActionGn(info);
  } else if (info.type == "ninja") {
    return _ActionNinja(info);
  }

  cerr << "Error action " << info.actname << endl;
  return false;
}

//-------------------//
bool _ActionShell(const sActionInfo& info) {
  cout << "-> dzlua " << info.actname << " " << info.name << " " << info.program << " " << info.arg << endl;
  return base::ExecuteShell(info.program, info.arg);
}

bool _ActionExplorer(const sActionInfo& info) {
  std::string arg = info.program + " " + info.arg;
  cout << "-> dzlua " << info.actname << " " << info.name << " " << arg << endl;
  std::string cmd = "explorer " + arg;
  system(cmd.c_str());
  return true;
}

bool _ActionGn(const sActionInfo& info) {
  std::unique_ptr<FilePath> path = FilePath::NewFilePath(info.program);
  if (!path->has_root_name()) {
    cerr << "Error action " << info.actname << "" << info.program << endl;
    return false;
  }

  // cd 
  std::string cmd = path->root_name()->string() + " && ";
  cmd += "cd " + path->string() + " && ";
  cmd += "gn gen " + info.arg;
  system(cmd.c_str());
  
  return true;
}

bool _ActionNinja(const sActionInfo& info) {
  std::unique_ptr<FilePath> path = FilePath::NewFilePath(info.program);
  if (!path->has_root_name()) {
    cerr << "Error action " << info.actname << "" << info.program << endl;
    return false;
  }

  // cd 
  std::string cmd = path->root_name()->string() + " && ";
  cmd += "cd " + path->string() + " && ";
  cmd += "ninja -C " + info.arg;
  system(cmd.c_str());
  
  return true;
}